#include<iostream>
#include<thread>
#include<cerrno>
#include<cstring>
#include<unistd.h>
#include<time.h>
#include<cassert>
#include<cstdio>
#include<pthread.h>

using namespace std;
#define NUM 10    //线程的个数,并不是线程越多，票抢得越快,

int tickets=10000;

class ThreadDate
{
public:
    ThreadDate(const string &n,pthread_mutex_t*pm):tname(n),pmtx(pm)
    {}
    ~ThreadDate()
    {}
public:
    string tname;
    pthread_mutex_t *pmtx;
};

void*getTickets(void*args)  //抢票操作
{
    ThreadDate*td=(ThreadDate*)args;

    while(1)
    {
        //抢票
        int n=pthread_mutex_lock(td->pmtx);  //加锁
        assert(n==0);
        
        //临界区
        if(tickets>0)
        {
            usleep(rand()%1500);
            printf("%s:%d\n",td->tname.c_str(),tickets);
            tickets--;
            n=pthread_mutex_unlock(td->pmtx);    //解锁
            assert(n==0);

        }
        else
        {
            n=pthread_mutex_unlock(td->pmtx);  //如果票数小于等于0时接触当前线程的锁，并且退出，就不会造成票数为负数，也不会出现多个线程抢一个票，避免函数重入
            assert(n==0);
            break;
        }
        usleep(rand()%2000);  
    }
    delete td;
    return nullptr;
}


int main()
{
    time_t start=time(nullptr);
    pthread_mutex_t mtx;    //申请一个锁
    pthread_mutex_init(&mtx,nullptr);  //初始化一个锁

    srand((unsigned long)time(nullptr)^getpid()^(0x147));   //种一个随机数种子,这里是为了让数字随机，区别变大，为了不让同一个线程把票抢完
    pthread_t t[NUM];

    //多线程抢票
    for(int i=0;i<NUM;i++)
    {
        string name="thread";
        name+=to_string(i+1);
        ThreadDate*td=new ThreadDate(name,&mtx);
        pthread_create(t+i,nullptr,getTickets,(void*)td); //这里的最后一个参数可以是任意类型的，当然也就可以是自定义类型的
    }


    for(int i=0;i<NUM;i++)
    {
        pthread_join(t[i],nullptr);
    }

    time_t end=time(nullptr);
    cout<<"cast:"<<(int)(end-start)<<"S"<<endl;


    return 0;
}